<!DOCTYPE html>
<html>
<head hexo-theme='https://volantis.js.org/#3.0-beta2'>
  <meta charset="utf-8">
  <!-- SEO相关 -->
  
    
  
  <!-- 渲染优化 -->
  <meta name="renderer" content="webkit">
  <meta name="force-rendering" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="HandheldFriendly" content="True" >
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  <!-- 页面元数据 -->
  
  <title>vue2.x VS vue3.0 - 河豚吞吞</title>
  
    <meta name="keywords" content="Vue.js">
  

  
    <meta name="description" content="There is no point me doing this if I can’t be myself.">
  

  <!-- feed -->
  

  <!-- import meta -->
  
    
      <meta name="msapplication-TileColor" content="#ffffff">
    
      <meta name="msapplication-config" content="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/blog/favicon/browserconfig.xml">
    
      <meta name="theme-color" content="#ffffff">
    
  

  <!-- link -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13/css/all.min.css">
  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css">


  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.css">

  

  

  
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer@1.10/dist/APlayer.min.css">
  

  

  <!-- import link -->
  
  
  <link rel="apple-touch-icon" sizes="180x180" href="/lemonElf/img/icon/icon001.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/lemonElf/img/icon/icon001.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/lemonElf/img/icon/icon001.png">
  <link rel="manifest" href="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/blog/favicon/site.webmanifest">
  <link rel="mask-icon" href="/lemonElf/img/icon/icon001.png" color="#5bbad5">
  <link rel="shortcut icon" href="/lemonElf/img/icon/icon001.png">
  

  
  
    
<link rel="stylesheet" href="/lemonelf/css/style.css">

  

  
  
</head>

<body>
  
  
  
  <div class="cover-wrapper">
    <cover class='cover post' style="display: none;">
      <div class='cover-body'>
  <div class='a'>
    
    
      <p class="title">MissGlobeFish</p>
    
    
      <p class="subtitle">When the world turns its back on you, you turn your back on the world! And only embrace whats next!</p>
    
  </div>
  <div class='b'>
    <div class='menu navigation dock'>
      <div class='list-h'>
        
          
            <a href="/lemonelf/"
              
              
              id="lemonelf">
              <img src='https://i.loli.net/2020/07/24/skczZ1VdMGRbTLi.png'><p>主页</p>
            </a>
          
            <a href="/lemonelf/archives/"
              
              
              id="lemonelfarchives">
              <img src='https://i.loli.net/2020/07/24/GaeXvVAdtJkCYT3.png'><p>归档</p>
            </a>
          
            <a href="/lemonelf/categories/"
              
              
              id="lemonelfcategories">
              <img src='https://i.loli.net/2020/07/24/9kHJZNA8m4RBxhp.png'><p>分类</p>
            </a>
          
            <a href="/lemonelf/friends/"
              
              
              id="lemonelffriends">
              <img src='https://i.loli.net/2020/07/24/cxRslyWzk2OTP9r.png'><p>友链</p>
            </a>
          
        
      </div>
    </div>
  </div>
</div>

      <div class="scroll-down" style="display: none;"><i class="fa fa-chevron-down scroll-down-effects"></i></div>
    </cover>
    <header class="l_header shadow blur">
  <div class='container'>
  <div class='wrapper'>
    <div class='nav-sub'>
      <p class="title"></p>
      <ul class='switcher nav-list-h' id="pjax-header-nav-list">
        <li><a class="s-comment fas fa-comments fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
          <li><a class="s-toc fas fa-list fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
      </ul>
    </div>
		<div class="nav-main">
      
        
        <a class="title flat-box" target="_self" href='/lemonelf/'>
          
          
          
            MissGlobeFish
          
        </a>
      

			<div class='menu navigation'>
				<ul class='nav-list-h'>
          
          
          
            
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/categories/
                  
                  
                  
                    id="lemonelfcategories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/tags/
                  
                  
                  
                    id="lemonelftags"
                  >
                  <i class='fas fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/archives/
                  
                  
                  
                    id="lemonelfarchives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/friends/
                  
                  
                  
                    id="lemonelffriends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/about/
                  
                  
                  
                    id="lemonelfabout"
                  >
                  <i class='fas fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
          
				</ul>
			</div>

      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <i class="icon fas fa-search fa-fw"></i>
          <input type="text" class="input u-search-input" placeholder="Search..." />
        </form>
      </div>

			<ul class='switcher nav-list-h'>
				
					<li><a class="s-search fas fa-search fa-fw" target="_self" href='javascript:void(0)'></a></li>
				
				<li>
          <a class="s-menu fas fa-bars fa-fw" target="_self" href='javascript:void(0)'></a>
          <ul class="menu-phone list-v navigation white-box">
            
              
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/categories/
                  
                  
                  
                    id="lemonelfcategories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/tags/
                  
                  
                  
                    id="lemonelftags"
                  >
                  <i class='fas fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/archives/
                  
                  
                  
                    id="lemonelfarchives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/friends/
                  
                  
                  
                    id="lemonelffriends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box" href=/lemonelf/about/
                  
                  
                  
                    id="lemonelfabout"
                  >
                  <i class='fas fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
            
          </ul>
        </li>
			</ul>
		</div>
	</div>
  </div>
</header>

  </div>
  


  <div class="l_body nocover">
    <div class='body-wrapper' id="pjax-container">
      
        <!--此文件用来存放一些不方便取值的变量-->
<!--思路大概是将值藏到重加载的区域内-->



<div id="pjax-data" style="display: none">
  <div id="pjax-ispage">true</div>
  <div id="pjax-pageTitle">vue2.x VS vue3.0</div>
  <div id="pjax-enable-cover">true</div>
  <div id="pjax-permalink">https://missglobefish.gitee.io/lemonelf/2020/08/25/vue2.x%20VS%20vue3.0/</div>
  
  <div id="valine-path">none</div>
  <div id="valine-placeholder">none</div>
  
  
</div>


<script>
  // 处理封面 此时 jquery 还没加载
  if ("none" == "none") { // 移除封面
    document.getElementsByClassName('cover')[0].style.display = "none";
    document.getElementsByClassName('l_body')[0].classList.add("nocover");
    document.getElementsByClassName('l_header', 'cover-wrapper')[0].classList.add("show");
  } else {
    if ("none" == "blog") { // 半屏
      document.getElementsByClassName('cover')[0].classList.remove("full");
      document.getElementsByClassName('cover')[0].classList.add("half");
      document.getElementsByClassName('scroll-down')[0].style.display = "none";
    } else if ("none" == "docs") { // 全屏
      document.getElementsByClassName('cover')[0].classList.remove("half");
      document.getElementsByClassName('cover')[0].classList.add("full");
      document.getElementsByClassName('scroll-down')[0].style.display = "";
    }
    document.getElementsByClassName('cover')[0].style.display = "";
    document.getElementsByClassName('l_body', 'show')[0].classList.remove("nocover");
    document.getElementsByClassName('l_header', 'cover-wrapper')[0].classList.remove("show");
  }
</script>

      
      

<div class='l_main'>
  

  
    <article id="post" class="post white-box reveal shadow article-type-post" itemscope itemprop="blogPost">
      


  <section class='meta'>
    
      
      
        <a title='vue2.x VS vue3.0' href='/lemonelf/2020/08/25/vue2.x%20VS%20vue3.0/'><img class='thumbnail' src='/lemonElf/img/icon/icon001.png'></a>
      
      <div class="meta" id="header-meta">
        
          
  <h1 class="title">
    <a href="/lemonelf/2020/08/25/vue2.x%20VS%20vue3.0/">
      vue2.x VS vue3.0
    </a>
  </h1>


        
        <div class='new-meta-box'>
          
            
          
            
              
<div class='new-meta-item author'>
  <a class='author' href="/" rel="nofollow">
    <img no-lazy src="https://pic4.zhimg.com/80/v2-52584915d42d676148e2c302f4e87382_hd.jpg">
    <p>MissGlobeFish</p>
  </a>
</div>

            
          
            
              
  
  <div class='new-meta-item category'>
    <a href='/lemonelf/categories/Vue-js/' rel="nofollow">
      <i class="fas fa-folder-open fa-fw" aria-hidden="true"></i>
      <p>Vue.js</p>
    </a>
  </div>


            
          
            
              <div class="new-meta-item date">
  <a class='notlink'>
    <i class="fas fa-calendar-alt fa-fw" aria-hidden="true"></i>
    <p>发布于：2020-08-25</p>
  </a>
</div>

            
          
            
              

            
          
        </div>
        
          <hr>
        
      </div>
    
  </section>


      <section class="article typo">
        <div class="article-entry" itemprop="articleBody">
          
          
          <p>There is no point me doing this if I can’t be myself.</p>
<a id="more"></a>

<p>本文将会全方位的记录 <code>Vue3</code> 的新功能，新优化，新特性，以及升级指南。</p>
<blockquote>
<p><strong>Vue3 会带来些什么？</strong></p>
</blockquote>
<h4 id="1-更快"><a href="#1-更快" class="headerlink" title="1. 更快"></a>1. 更快</h4><h5 id="1-1-重构了-Virtual-DOM"><a href="#1-1-重构了-Virtual-DOM" class="headerlink" title="1.1 重构了 Virtual DOM"></a>1.1 重构了 Virtual DOM</h5><p><code>Vue3</code> 重写了虚拟 <code>DOM</code> 的实现方法，初始渲染/更新可以提速达 100%。</p>
<p>对于 <code>Vue2.x</code> 版本的虚拟 <code>DOM</code> 来说，<code>Vue</code> 会遍历 <code>&lt;template&gt;</code> 模板中的所有内容，并根据这些标签生成对应的虚拟 <code>DOM</code>（虚拟 <code>DOM</code> 一般指采用 <code>key/value</code> 对象来保存标签元素的属性和内容），</p>
<p>当有内容改变时，遍历虚拟 <code>DOM</code> 来 <code>diff</code> ( 比较差异 ) 找到对应的标签元素所对应的 <code>DOM</code> 节点，并改变其内容。</p>
<p>例如下面这段内容：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;content&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span> number1 <span class="tag">&lt;/<span class="name">p</span>&gt;</span>   </span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span> number2 <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span> number3 <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span> &#123;&#123;count&#125;&#125; <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>当触发双向绑定时，遍历所有的 <code>&lt;div&gt;</code> 标签和 <code>&lt;p&gt;</code> 标签，找到 <code>&#123;&#123;count&#125;&#125;</code> 变量对应的 <code>&lt;p&gt;</code> 的 <code>DOM</code> 节点，并改变其内容。</p>
<p>这对于那些纯静态 <code>&lt;p&gt;</code> 的节点进行 <code>diff</code> 其实是比较浪费资源的，当节点的数量很少时，表现并不明显，但是一旦节点的数量过大，在性能上就会慢很多。</p>
<p>对此，<code>Vue3</code> 在此基础上进行了优化主要有：</p>
<ol>
<li>标记静态内容，并区分动态内容。</li>
<li>更新时只 diff 动态的部分。</li>
</ol>
<p>针对上面的代码，<code>Vue3</code> 中首先会区分出 <code>&#123;&#123;count&#125;&#125;</code> 这部分动态的节点，在进行 <code>diff</code> 时，只针对这些节点进行，从而减少资源浪费，提升性能。</p>
<h5 id="1-2-事件缓存"><a href="#1-2-事件缓存" class="headerlink" title="1.2 事件缓存"></a>1.2 事件缓存</h5><p>在 <code>vue2</code> 中，针对节点绑定的事件，每次触发都要重新生成全新的 <code>function</code> 去更新。</p>
<p>在 <code>Vue3</code> 中，提供了事件缓存对象 <code>cacheHandlers</code>，当 <code>cacheHandlers</code> 开启的时候，编译会自动生成一个内联函数，将其变成一个静态节点，这样当事件再次触发时，就无需重新创建函数直接调用缓存的事件回调方法即可。</p>
<h5 id="1-3-基于-Proxy-的响应式对象"><a href="#1-3-基于-Proxy-的响应式对象" class="headerlink" title="1.3 基于 Proxy 的响应式对象"></a>1.3 基于 Proxy 的响应式对象</h5><p><code>Proxy API</code> 对应的 <code>Proxy</code> 对象是 <code>ES2015</code> 就已引入的一个原生对象，用于定义基本操作的自定义行为（如属性查找、赋值、枚举、函数调用等）。</p>
<p><code>Proxy</code> 对象是目标对象的一个代理器，任何对目标对象的操作（实例化，添加/删除/修改属性等等），都必须通过该代理器。</p>
<p>因此我们可以把来自外界的所有操作进行拦截和过滤或者修改等操作。</p>
<p>例如下面的示例：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> foo = &#123;</span><br><span class="line">    a: <span class="number">1</span>,</span><br><span class="line">    b: <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> handler = &#123;</span><br><span class="line">    set(obj, key, value)=&gt;&#123;</span><br><span class="line">        conosle.log(<span class="string">&#x27;set&#x27;</span>);</span><br><span class="line">        obj[key] = value;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> p = <span class="keyword">new</span> <span class="built_in">Proxy</span>(foo, handler)</span><br><span class="line">p.a = <span class="number">3</span>; <span class="comment">// 打印出 console.log(&#x27;set&#x27;)</span></span><br></pre></td></tr></table></figure>
<p>在 <code>Vue2.x</code> 中，使用 <code>Object.defineProperty()</code> 来实现响应式对象，对于一些复杂的对象，还需要循环递归的给每个属性增加上 <code>getter/setter</code> 监听器，这使得组件的初始化非常耗时。</p>
<p>而 <code>Vue3</code> 中，<code>composition-api</code> 提供了一种创建响应式对象的方法 <code>reactive</code>，其内部就是利用了 <code>Proxy API</code> 来实现的，这样就可以不用针对每个属性来一一进行添加，减少开销提升性能。</p>
<h4 id="2-更小"><a href="#2-更小" class="headerlink" title="2. 更小"></a>2. 更小</h4><h5 id="2-1-Tree-shaking-支持"><a href="#2-1-Tree-shaking-支持" class="headerlink" title="2.1 Tree shaking 支持"></a>2.1 Tree shaking 支持</h5><p><code>Tree shaking</code> 是一个术语，通常用于描述移除 <code>JavaScript</code> 上下文中的未引用代码( dead-code )，就像一棵大树，将那些无用的叶子都摇掉。</p>
<p>它依赖于 <code>ES2015</code> 模块语法的 <code>静态结构</code> 特性，例如 <code>import</code> 和 <code>export</code>。这个术语和概念在打包工具 <code>rollup</code> 和 <code>wepack</code> 中普及开来。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;get&#125; form <span class="string">&#x27;./api.js&#x27;</span></span><br><span class="line"><span class="keyword">let</span> dosome = <span class="function">()=&gt;</span>&#123;</span><br><span class="line">    get();</span><br><span class="line">&#125;</span><br><span class="line">dosome();</span><br></pre></td></tr></table></figure>
<p><code>api.js</code> 代码：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> post = <span class="function">()=&gt;</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;post&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> post;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> get = <span class="function">()=&gt;</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;get&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> get;</span><br></pre></td></tr></table></figure>
<p>上面代码中，<code>api.js</code> 代码中的 <code>post</code> 方法相关内容是没有被引入和使用的，有了 <code>Tree shaking</code> 之后，这部分内容是不会被打包的，这就在一定程度上减少了资源的大小。</p>
<p>使用 <code>Tree shaking</code> 的原理是 <code>ES6</code> 的模块静态分析引入，这就可以在编译时正确判断到底加载了什么代码，但是要注意 <code>import</code> 和 <code>export</code> 是 <code>ES6</code> 原生的而不是通过 <code>babel</code> 或者 <code>webpack</code> 转化的。</p>
<p>在 <code>Vue3</code> 中，对代码结构进行了优化，让其更加符合 <code>Tree shaking</code> 的结构，这样使用相关的 <code>api</code> 时，不会把所有的都打包进来，只会打包你用到的 <code>api</code>。</p>
<p>例如：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- vue <span class="number">2.</span>x --&gt;</span><br><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span><br><span class="line"><span class="keyword">new</span> Vue()</span><br><span class="line">Vue.nextTick(<span class="function">() =&gt;</span> &#123;&#125;)</span><br><span class="line"><span class="keyword">const</span> obj = Vue.observable(&#123;&#125;)</span><br><span class="line"></span><br><span class="line">&lt;!-- vue <span class="number">3.</span>x --&gt;</span><br><span class="line"><span class="keyword">import</span> &#123; nextTick, observable,createApp &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span><br><span class="line">nextTick(<span class="function">() =&gt;</span> &#123;&#125;)</span><br><span class="line"><span class="keyword">const</span> obj = observable(&#123;&#125;)</span><br><span class="line">createApp(&#123;&#125;)</span><br></pre></td></tr></table></figure>
<p>同时，例如 <code>&lt;keep-alive&gt;</code> 和 <code>&lt;transition&gt;</code>, <code>&lt;teleport&gt;</code> 等内置组件，如果没有使用也不会被打包到资源里。</p>
<h4 id="3-更易于维护"><a href="#3-更易于维护" class="headerlink" title="3. 更易于维护"></a>3. 更易于维护</h4><h5 id="3-1-Vue3-从-Flow-迁移到-TypeScript"><a href="#3-1-Vue3-从-Flow-迁移到-TypeScript" class="headerlink" title="3.1 Vue3 从 Flow 迁移到 TypeScript"></a>3.1 Vue3 从 Flow 迁移到 TypeScript</h5><p>在 <code>Vue3</code> 的源码结构层面，从 <code>Flow</code> 改成了 <code>TypeScript</code> 来编写，一般来说对于 <code>JavaScript</code> 源码框架来说引入类型检测是非常重要的，不仅可以减少 <code>bug</code> 的产生，还可以规范一些接口的定义;</p>
<p><code>Flowfacebook</code> 出品，是一个静态类型检测器，有了它就可以在 <code>JavaScript</code> 运行前找出常见的 <code>bug</code> 。</p>
<p>包括：</p>
<ol>
<li>自动类型转换</li>
<li>null 引用</li>
<li>可怕的 undefined is not a function</li>
</ol>
<p>例如：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @flow</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">x:number</span>):<span class="title">number</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> x + <span class="number">10</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">foo(<span class="string">&#x27;hi&#x27;</span>); <span class="comment">// 参数 x 需要为 number 类型，否则就会报错</span></span><br><span class="line"></span><br><span class="line">message: <span class="string">&#x27;[flow] string (This type is incompatible with number See also: function call)&#x27;</span></span><br></pre></td></tr></table></figure>
<p>这些特性和 <code>typescript</code> 非常吻合，所以在 <code>Vue3</code> 中直接采用了 <code>typescript</code> 来进行重写，从源码层面来提升项目的可维护性。</p>
<h5 id="3-2-代码目录结构遵循-monorepo"><a href="#3-2-代码目录结构遵循-monorepo" class="headerlink" title="3.2 代码目录结构遵循 monorepo"></a>3.2 代码目录结构遵循 monorepo</h5><p><code>monorepo</code> 是一种管理代码的方式，它的核心观点是所有的项目在一个代码仓库中，但是代码分割到一个个小的模块中，而不是都放在 <code>src</code> 这个目录下面。</p>
<p>这样的分割，每个开发者大部分时只是工作在少数的几个文件夹以内的，并且也只会编译自己负责的模块，而且不会导致一个 <code>IDE</code> 打不开太大的项目之类的事情，这样很多事情就简单了很多。</p>
<p>如下图：<br><img src="/lemonElf/img/vue/vue03.png" alt="代码管理变更"><br>目前诸如 <code>Babel</code>, <code>React</code>, <code>Angular</code>, <code>Ember</code>, <code>Meteor</code>, <code>Jest</code> 等等都采用了 <code>Monorepo</code> 这种方式来进行源码的管理。</p>
<p><code>Vue2.x</code> 的目录结构：</p>
<p><img src="/lemonElf/img/vue/vue04.png" alt="Vue2.x 目录结构"></p>
<p><code>Vue3</code> 的目录结构：</p>
<p><img src="/lemonElf/img/vue/vue05.png" alt="Vue3 目录结构"></p>
<h4 id="4-新功能和特性"><a href="#4-新功能和特性" class="headerlink" title="4. 新功能和特性"></a>4. 新功能和特性</h4><h5 id="4-1-Composition-API"><a href="#4-1-Composition-API" class="headerlink" title="4.1 Composition API"></a>4.1 Composition API</h5><p>在 <code>Vue2.x</code> 中，组件的主要逻辑是通过一些配置项来编写，包括一些内置的生命周期方法或者组件方法，例如下面的代码：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">    name: <span class="string">&#x27;test&#x27;</span> ,</span><br><span class="line">    components: &#123;&#125;,</span><br><span class="line">    props: &#123;&#125;,</span><br><span class="line">    data()&#123;</span><br><span class="line">        <span class="keyword">return</span>&#123;&#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    created()&#123;&#125;,</span><br><span class="line">    mounted()&#123;&#125;,</span><br><span class="line">    watch:&#123;&#125;,</span><br><span class="line">    methods:&#123;&#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这中基于配置的组件写法成为 <code>Options API</code>（ 配置式 API ），<code>Vue3</code> 的一大核心特性是引入了<code> Composition API</code>（ 组合式 API ）,</p>
<p>这使得组件的大部分内容都可以通过 <code>setup()</code> 方法进行配置，同时 <code>Composition API</code> 在 <code>Vue2.x</code> 也可以使用，需要通过安装 <code>@vue/composition-api</code> 来使用。</p>
<p>代码如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">npm install @vue/composition-api</span><br><span class="line">...</span><br><span class="line"><span class="keyword">import</span> VueCompositionApi <span class="keyword">from</span> <span class="string">&#x27;@vue/composition-api&#x27;</span>;</span><br><span class="line">Vue use(VueCompositionApi);</span><br></pre></td></tr></table></figure>
<p>下面就列举一些使用 <code>Composition API</code> 的例子：</p>
<p>【 一 】 <code>ref</code> 或者 <code>reactive</code> 代替 <code>data</code> 中的变量</p>
<p>在 <code>Vue2.x</code> 中通过组件 <code>data</code> 的方法来定义一些当前组件的数据：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">data() &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        name: <span class="string">&#x27;test&#x27;</span>,</span><br><span class="line">        list: [],</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>在 <code>Vue3</code> 中通过 <code>ref</code> 或者 <code>reactive</code> 创建响应式对象：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;ref, reactive&#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span><br><span class="line">...</span><br><span class="line">setup()&#123;</span><br><span class="line">    <span class="keyword">const</span> name = ref(<span class="string">&#x27;test&#x27;</span>)</span><br><span class="line">    <span class="keyword">const</span> state = reactive(&#123;</span><br><span class="line">        list: []</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        name,</span><br><span class="line">        state</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>ref</code> 将给定的值创建一个响应式的数据对象并赋值初始值（ int 或者 string ），<code>reactive</code> 可以直接定义复杂响应式对象。</p>
<p>【 二 】 <code>methods</code> 中定义的方法也可以写在 <code>setup()</code> 中</p>
<p>在 <code>Vue2.x</code> 中 <code>methods</code> 来定义一些当前组件内部方法：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">methods: &#123;</span><br><span class="line">    fetchData() &#123;</span><br><span class="line"></span><br><span class="line">    &#125;,</span><br><span class="line">&#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>在 <code>Vue3</code> 中直接在 <code>setup</code> 方法中定义并 <code>return</code>：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">···</span><br><span class="line">setup()&#123;</span><br><span class="line">    <span class="keyword">const</span> fetchData = <span class="function">()=&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;fetchData&#x27;</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        fetchData</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">···</span><br></pre></td></tr></table></figure>

<p>【 三 】 <code>setup()</code> 中使用 <code>props</code> 和 <code>this</code></p>
<p>在 <code>Vue2.x</code> 中，组件的方法中可以通过 <code>this</code> 获取到当前组件的实例，并执行 <code>data</code> 变量的修改，方法的调用，组件的通信等等；</p>
<p>但是在 <code>Vue3</code> 中，<code>setup()</code> 在 <code>beforeCreate</code> 和 <code>created</code> 时机就已调用，无法使用和 <code>Vue2.x</code> 一样的 <code>this</code>；</p>
<p>但是可以通过接收 <code>setup( props, ctx )</code> 的方法，获取到当前组件的实例和 <code>props</code>：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span>&#123;</span><br><span class="line">    props: &#123;</span><br><span class="line">        name: <span class="built_in">String</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    setup( props, ctx )&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(props.name);</span><br><span class="line">        ctx.emit(<span class="string">&#x27;event&#x27;</span>)</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>注意 <code>ctx</code> 和 <code>2.x</code> 中 <code>this</code> 并不完全一样，而是选择性地暴露了一些 <code>property</code>，主要有 <code>[attrs,emit,slots]</code>。</p>
<p>【 四 】 <code>watch</code> 来监听对象改变</p>
<p><code>Vue2.x</code> 中，可以采用 <code>watch</code> 来监听一个对象属性是否有改动：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">···</span><br><span class="line">data()&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        name: <span class="string">&#x27;a&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;,</span><br><span class="line">watch:&#123;</span><br><span class="line">    name(val)&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(val)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">···</span><br></pre></td></tr></table></figure>
<p><code>Vue3</code> 中，在 <code>setup()</code> 中，可以使用 <code>watch</code> 来监听：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">···</span><br><span class="line"><span class="keyword">import</span> &#123;watch&#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span><br><span class="line">setup()&#123;</span><br><span class="line">    <span class="keyword">let</span> state = reactive(&#123;</span><br><span class="line">        name: <span class="string">&#x27;a&#x27;</span></span><br><span class="line">    &#125;)</span><br><span class="line">    watch(</span><br><span class="line">        () =&gt; state.name,</span><br><span class="line">        (val, oldVal) =&gt; &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(val)</span><br><span class="line">        &#125;</span><br><span class="line">    )</span><br><span class="line">    state.name = <span class="string">&#x27;b&#x27;</span></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        state</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">···</span><br></pre></td></tr></table></figure>
<p>在 <code>Vue3</code> 中，如果 <code>watch</code> 的是一个数组 <code>array</code> 对象，那么如果调用 <code>array.push()</code> 方法添加一条数据，并不会触发 <code>watch</code> 方法，必须重新给 <code>array</code> 赋值：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> state = reactive(&#123;</span><br><span class="line">    list: []</span><br><span class="line">&#125;)</span><br><span class="line">watch(</span><br><span class="line">    () =&gt; state.list,</span><br><span class="line">    (val, oldVal) =&gt; &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(val)</span><br><span class="line">    &#125;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">state.list.push(<span class="number">1</span>) <span class="comment">// 不会触发 watch</span></span><br><span class="line"></span><br><span class="line">state.list = [<span class="number">1</span>] <span class="comment">// 会触发 watch</span></span><br></pre></td></tr></table></figure>

<p>【 五 】 <code>computed</code> 计算属性</p>
<p><code>Vue2.x</code> 中：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">···</span><br><span class="line">computed: &#123;</span><br><span class="line">    storeData () &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.$store.state.storeData  </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">···</span><br></pre></td></tr></table></figure>
<p><code>Vue3</code> 中：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">···</span><br><span class="line"><span class="keyword">import</span> &#123;computed&#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span><br><span class="line">setup()&#123;</span><br><span class="line">    <span class="keyword">const</span> storeData = computed( <span class="function">() =&gt;</span> store.state.storeData )</span><br><span class="line">    <span class="keyword">return</span> storeData</span><br><span class="line">&#125;</span><br><span class="line">···</span><br></pre></td></tr></table></figure>

<p>【 六 】 <code>Fragments</code></p>
<p>在 <code>Vue2.x</code> 中， <code>&lt;template&gt;</code> 中的内容必须由一个最外层的父元素包裹，代码如下：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">header</span>&gt;</span>...<span class="tag">&lt;/<span class="name">header</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">main</span>&gt;</span>...<span class="tag">&lt;/<span class="name">main</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">footer</span>&gt;</span>...<span class="tag">&lt;/<span class="name">footer</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p><code>React</code> 社区也遇到了同样的问题，他们的解决方案是一个名为 <code>Fragment</code> 的虚拟元素，使用方法如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Hello</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span></span>&#123;</span><br><span class="line">    render ()&#123;</span><br><span class="line">        &lt;React.Fragment&gt;</span><br><span class="line">            &lt;header&gt;...&lt;/header&gt;</span><br><span class="line">            &lt;main&gt;...&lt;/main&gt;</span><br><span class="line">            &lt;footer&gt;...&lt;/footer&gt;</span><br><span class="line">        &lt;/React.Fragment&gt;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>而在 <code>Vue3</code> 中，使用更加简单，可以直接省略最外层的元素，写法如下：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">header</span>&gt;</span>...<span class="tag">&lt;/<span class="name">header</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">main</span>&gt;</span>...<span class="tag">&lt;/<span class="name">main</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">footer</span>&gt;</span>...<span class="tag">&lt;/<span class="name">footer</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>在 <code>Vue3</code> 中， <code>&lt;teleport&gt;</code> 是一个内置标签，如果你曾经创建过模态功能，你会知道它通常被放置在关闭的 <code>&lt;/body&gt;</code> 标签之前，如下：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--main page content here--&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--modal here--&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>如果按照以往的思路，需要将模态的 <code>UI</code> 代码放在底部，如下：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;app&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h3</span>&gt;</span>Tooltips with Vue 3 Teleport<span class="tag">&lt;/<span class="name">h3</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">modal-button</span>&gt;</span><span class="tag">&lt;/<span class="name">modal-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>这样做是因为模式通常具有覆盖页面的背景，要使用 <code>CSS</code> 来实现，您不需要处理父元素定位和 <code>z-index</code> 堆栈上下文，因此最简单的解决方案是将模式放在页面的最底部。</p>
<p>这样的话这部分逻辑就脱离了整个项目的跟组件 <code>App</code> 的管理，就造成直接采用 <code>JavaScript</code> 和 <code>CSS</code> 来修改 <code>UI</code>，并不是很规范。</p>
<p>为了允许将一些 <code>UI</code> 片段段移动到页面中的其他位置，在 <code>Vue3</code> 中添加了一个新的 <code>&lt;teleport&gt;</code> 组件。</p>
<p>要使用 <code>&lt;teleport&gt;</code>，首先要在页面上添加一个元素，我们要将模态内容移动到该页面。代码如下：</p>

          
            <div class='article_footer'>
              
                
  

  

  

  


              
            </div>
          
        </div>
        
          


  <section class='meta' id="footer-meta">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateUpdated" datetime="2020-08-25T22:04:39+08:00">
  <a class='notlink'>
    <i class="fas fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：2020-08-25</p>
  </a>
</div>

        
      
        
          
  
  <div class="new-meta-item meta-tags"><a class="tag" href="/lemonelf/tags/Vue-js/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>Vue.js</p></a></div>


        
      
        
          

        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=https://missglobefish.gitee.io/lemonelf/2020/08/25/vue2.x%20VS%20vue3.0/&title=vue2.x VS vue3.0 - 河豚吞吞&pics=/lemonElf/img/icon/icon001.png&summary=There is no point me doing this if I can’t be myself."
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qq.png">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=https://missglobefish.gitee.io/lemonelf/2020/08/25/vue2.x%20VS%20vue3.0/&title=vue2.x VS vue3.0 - 河豚吞吞&pics=/lemonElf/img/icon/icon001.png&summary=There is no point me doing this if I can’t be myself."
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qzone.png">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://service.weibo.com/share/share.php?url=https://missglobefish.gitee.io/lemonelf/2020/08/25/vue2.x%20VS%20vue3.0/&title=vue2.x VS vue3.0 - 河豚吞吞&pics=/lemonElf/img/icon/icon001.png&summary=There is no point me doing this if I can’t be myself."
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/weibo.png">
          
        </a>
      
    
  </div>
</div>



        
      
    </div>
  </section>


        
        
          <div class="prev-next">
            
              <a class='prev' href='/lemonelf/2021/05/11/Gulp%20%E6%9E%84%E5%BB%BA%E5%B7%A5%E5%85%B7/'>
                <p class='title'><i class="fas fa-chevron-left" aria-hidden="true"></i>Gulp 构建工具</p>
                <p class='content'>Artists lead and hacks ask for a show of hands.


node.js 利用 gulp 启动 webServer 服务。

先安装 node.js；
...</p>
              </a>
            
            
              <a class='next' href='/lemonelf/2020/08/15/ES6%20%E8%AF%AD%E6%B3%95%E7%9A%84%E6%89%A9%E5%B1%95/'>
                <p class='title'>ES6 语法的扩展<i class="fas fa-chevron-right" aria-hidden="true"></i></p>
                <p class='content'>The sky was blue when I met you!



字符串扩展

1. 字符串的扩展1.1 字符编码 – charCodeAt() | fromCharCodeAt()编码格...</p>
              </a>
            
          </div>
        
      </section>
    </article>
  




<!-- 根据页面mathjax变量决定是否加载MathJax数学公式js -->


</div>
<aside class='l_side'>
  
  

  
    
    



  <section class="widget toc-wrapper shadow desktop mobile" id="toc-div" >
    
  <header>
    
      <i class="fas fa-list fa-fw" aria-hidden="true"></i><span class='name'>本文目录</span>
    
  </header>


    <div class='content'>
        <ol class="toc"><li class="toc-item toc-level-4"><a class="toc-link" href="#1-%E6%9B%B4%E5%BF%AB"><span class="toc-text">1. 更快</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#1-1-%E9%87%8D%E6%9E%84%E4%BA%86-Virtual-DOM"><span class="toc-text">1.1 重构了 Virtual DOM</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#1-2-%E4%BA%8B%E4%BB%B6%E7%BC%93%E5%AD%98"><span class="toc-text">1.2 事件缓存</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#1-3-%E5%9F%BA%E4%BA%8E-Proxy-%E7%9A%84%E5%93%8D%E5%BA%94%E5%BC%8F%E5%AF%B9%E8%B1%A1"><span class="toc-text">1.3 基于 Proxy 的响应式对象</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-%E6%9B%B4%E5%B0%8F"><span class="toc-text">2. 更小</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#2-1-Tree-shaking-%E6%94%AF%E6%8C%81"><span class="toc-text">2.1 Tree shaking 支持</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-%E6%9B%B4%E6%98%93%E4%BA%8E%E7%BB%B4%E6%8A%A4"><span class="toc-text">3. 更易于维护</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#3-1-Vue3-%E4%BB%8E-Flow-%E8%BF%81%E7%A7%BB%E5%88%B0-TypeScript"><span class="toc-text">3.1 Vue3 从 Flow 迁移到 TypeScript</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#3-2-%E4%BB%A3%E7%A0%81%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84%E9%81%B5%E5%BE%AA-monorepo"><span class="toc-text">3.2 代码目录结构遵循 monorepo</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#4-%E6%96%B0%E5%8A%9F%E8%83%BD%E5%92%8C%E7%89%B9%E6%80%A7"><span class="toc-text">4. 新功能和特性</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#4-1-Composition-API"><span class="toc-text">4.1 Composition API</span></a></li></ol></li></ol>
    </div>
  </section>


  


</aside>



    </div>
    
  
  <footer class="clearfix">
    <br><br>
    
      
        <div class="aplayer-container">
          

  
    <meting-js
      theme='#1BCDFC'
      autoplay='false'
      volume='0.7'
      loop='all'
      order='list'
      fixed='false'
      list-max-height='320px'
      server='netease'
      type='playlist'
      id='3175833810'
      list-folded='true'>
    </meting-js>
  


        </div>
      
    
      
        <br>
        <div class="social-wrapper">
          
            
              <a href="https://missglobefish.github.io/"
                class="social fas fa-rss flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
            
              <a href="https://missglobefish.github.io/"
                class="social fas fa-envelope flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
            
              <a href="https://gitee.com/MissGlobeFish"
                class="social fab fa-github flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
            
              <a href="https://music.163.com/#/user/home?id=63035382"
                class="social fas fa-headphones-alt flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
        </div>
      
    
      
        <div><p>Blog content follows the <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en">Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) License</a></p>
</div>
      
    
      
        Use
        <a href="https://volantis.js.org/" target="_blank" class="codename">Volantis</a>
        as theme, total visits
          <span id="busuanzi_value_site_pv"><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span>
          times
        
      
    
      
        <div class='copyright'>
        <p><a href="/">Copyright © 2017-2020 XXX</a></p>

        </div>
      
    
  </footer>


    <a class="s-top fas fa-arrow-up fa-fw" href='javascript:void(0)'></a>
  </div>
  <div>
    
      
  

  <div id="rightmenu-wrapper">
    <ul class="list-v rightmenu" id="rightmenu-content">
      <li class='option'>
        <a class='vlts-menu opt fix-cursor-default' id='menu-copy-text' onclick='document.execCommand("copy")'><i class='fa fa-copy fa-fw'></i>Copy Text</a>
        <hr id='hr-text'>
        <a class='vlts-menu opt fix-cursor-default' id='menu-copy-href'><i class='fa fa-link fa-fw'></i>Copy Link</a>
        <a class='vlts-menu opt fix-cursor-default' id='menu-open-href'><i class='fa fa-external-link-square-alt fa-fw'></i>Open Link in New Tab</a>
        <hr id='hr-href'>
        <a class='vlts-menu opt fix-cursor-default' id='menu-copy-src'><i class='fa fa-image fa-fw'></i>Copy Image Link</a>
        <hr id='hr-src'>
      </li>
      <li class='navigation'>
        <a class='nav icon-only fix-cursor-default' onclick='history.back()'><i class='fa fa-arrow-left fa-fw'></i></a>
        <a class='nav icon-only fix-cursor-default' onclick='history.forward()'><i class='fa fa-arrow-right fa-fw'></i></a>
        <a class='nav icon-only fix-cursor-default' onclick='window.location.reload()'><i class='fa fa-redo fa-fw'></i></a>
        <a class='nav icon-only fix-cursor-default' href='/'><i class='fa fa-home fa-fw'></i></a>
      </li>
      <hr>
      
        
      
        
          
    <li>
      <a class='vlts-menu fix-cursor-default' href=https://gitee.com/MissGlobeFish/lemonElf
        
        
        
        
          id="https:giteecomMissGlobeFishlemonElf"
        >
        <i class='fas fa-envelope fa-fw'></i> 提出问题
      </a>
    </li>
  
        
      
        
      
        
          
    <li>
      <a class='vlts-menu fix-cursor-default' href=https://missglobefish.github.io/
        
        
        
        
          id="https:missglobefishgithubio"
        >
        <i class='fa fa-fan fa-spin fa-fw'></i> 生活博客
      </a>
    </li>
  
        
      
        
          <hr>
        
      
        
          
    <li>
      <a class='vlts-menu fix-cursor-default' href=https://gitee.com/MissGlobeFish/lemonElf
        
        
        
        
          id="https:giteecomMissGlobeFishlemonElf"
        >
        <i class='fa fa-code-branch fa-fw'></i> 本站源码
      </a>
    </li>
  
        
      
        
      
        
          <hr>
        
      
        
          
    <li>
      <a class='vlts-menu fix-cursor-default' 
        
        
        
          onclick="document.execCommand('print')"
        
        >
        <i class='fa fa-print fa-fw'></i> 打印页面
      </a>
    </li>
  
        
      
        
          <hr>
        
      
        
          <li class='music name'>
            <p class='nav music-title fix-cursor-default'></p>
          </li>
          <li class='music ctrl'>
            <a class='nav icon-only backward fix-cursor-default' onclick='aplayerBackward()'><i class='fa fa-step-backward fa-fw'></i></a>
            <a class='nav icon-only toggle fix-cursor-default' onclick='aplayerToggle()'><i class='fa fa-play fa-fw'></i></a>
            <a class='nav icon-only forward fix-cursor-default' onclick='aplayerForward()'><i class='fa fa-step-forward fa-fw'></i></a>
          </li>
          <li class='music volume'>
            <a class='nav volume'>
              <div class="aplayer-volume-bar-wrap">
                <div class="aplayer-volume-bar fix-cursor-pointer">
                  <div class="aplayer-volume"></div>
                  <i class='left fa fa-volume-off fa-fw'></i>
                  <i class='right fa fa-volume-up fa-fw'></i>
                </div>
              </div>
            </a>
          </li>
        
      
    </ul>
  </div>

  <script>
    window.document.oncontextmenu = function (event) {
      if (event.ctrlKey) return true;
      if (/Android|webOS|BlackBerry/i.test(navigator.userAgent)) return true;
      return popMenu(event);
    };
    document.addEventListener("click", function (event) {
      var mymenu = document.getElementById('rightmenu-wrapper');
      mymenu.style.display = "none";
    });
    function popMenu(event) {
      var mymenu = document.getElementById('rightmenu-wrapper');
      var menuContent = document.getElementById('rightmenu-content');
      var screenWidth = document.documentElement.clientWidth || document.body.clientWidth;
      var screenHeight = document.documentElement.clientHeight || document.body.clientHeight;
      mymenu.style.left = event.clientX + "px"; // 获取鼠标位置
      mymenu.style.top = event.clientY + "px";
      mymenu.style.display = 'block';
      if (event.clientX * 2 > screenWidth) {
        menuContent.classList.add('left');
      } else {
        menuContent.classList.remove('left');
      }
      if (event.clientY * 2 > screenHeight) {
        menuContent.classList.add('top');
      } else {
        menuContent.classList.remove('top');
      }

      let hrText = document.getElementById('hr-text');
      let hrSrc = document.getElementById('hr-src');
      let hrHref = document.getElementById('hr-href');

      // 选中图片
      let copySrc = document.getElementById('menu-copy-src');
      if (copySrc != undefined) {
        if (event.target.currentSrc) {
          copySrc.style.display = 'block';
          copySrc.addEventListener("click", function (e) {
            copyString(event.target.currentSrc);
          },{once: true});
          hrSrc.style.display = 'block';
        } else {
          copySrc.style.display = 'none';
          hrSrc.style.display = 'none';
        }
      }

      // 选中按钮
      // 判断是不是按钮
      let href = '';
      if (event.path) {
        for (i = 0; i < event.path.length; i++) {
          if (event.path[i].href != undefined && event.path[i].href.length > 0) {
            href = event.path[i].href;
          }
        }
      }

      let copyText = document.getElementById('menu-copy-text');
      copyText.style.display = 'none';
      hrText.style.display = 'none';
      if (href.length == 0) {
        // 选中文本
        if (window.getSelection().toString()) {
          copyText.style.display = 'block';
          hrText.style.display = 'block';
        }
      }

      // 在新标签页打开
      let openHref = document.getElementById('menu-open-href');
      if (openHref != undefined) {
        if (href.length > 0) {
          openHref.style.display = 'block';
          openHref.addEventListener("click", function (e) {
            window.open(href);
          },{once: true});
          hrHref.style.display = 'block';
        } else {
          openHref.style.display = 'none';
          hrHref.style.display = 'none';
        }
      }
      // 复制链接
      let copyHref = document.getElementById('menu-copy-href');
      if (copyHref != undefined) {
        if (href.length > 0) {
          copyHref.style.display = 'block';
          copyHref.addEventListener("click", function (e) {
            copyString(href);
          },{once: true});
        } else {
          copyHref.style.display = 'none';
        }
      }

      // 有音乐播放器
      if (true == true) {
        // 如果有aplayer，初始化一下
      	try {
      		checkAPlayer();
          updateTitle();
      	} catch (error) {
      		console.log(error);
      	}
      }

      return false; // 该行代码确保系统自带的右键菜单不会被调出
    }
    function hideMenu() {
      document.getElementById('rightmenu-wrapper').style.display = 'none';
    }
    function copyString(str) {
      const input = document.createElement('input');
      input.setAttribute('readonly', 'readonly');
      document.body.appendChild(input);
      input.setAttribute('value', str);
      input.select();
      document.execCommand('copy');
      document.body.removeChild(input);
    }
    document.execCommand('click');
  </script>


    
    
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5/dist/jquery.min.js"></script>



  <script>
    
    var SEARCH_SERVICE = "hexo" || "hexo";
    var ROOT = "/lemonelf/" || "/";
    if (!ROOT.endsWith('/')) ROOT += '/';
  </script>





  <script async src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@2/js/instant_page.js" type="module" defer integrity="sha384-OeDn4XE77tdHo8pGtE1apMPmAipjoxUQ++eeJa6EtJCfHlvijigWiJpD7VDPWXV1"></script>



  
  
  
    
<script src="https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js"></script>

    <script type="text/javascript">
      $(function(){
        var imgs=["https://gitee.com/wallpapers/minimalist/raw/master/2020/001.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/002.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/003.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/004.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/005.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/006.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/012.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/016.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/019.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/025.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/033.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/034.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/035.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/038.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/039.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/042.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/042.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/046.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/051.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/052.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/054.jpg", "https://gitee.com/wallpapers/minimalist/raw/master/2020/056.jpg"];
        if ('true' == 'true') {
          function shuffle(arr){
            /*From countercurrent-time*/
            var n = arr.length;
            while(n--) {
              var index = Math.floor(Math.random() * n);
              var temp = arr[index];
              arr[index] = arr[n];
              arr[n] = temp;
            }
          }
          shuffle(imgs);
        }
        if ('.cover') {
          $('.cover').backstretch(
            imgs,
          {
            duration: "20000",
            fade: "1500"
          });
        } else {
          $.backstretch(
            imgs,
          {
            duration: "20000",
            fade: "1500"
          });
        }
      });
    </script>
  



  <script>
    let APlayerController = new Object();
    APlayerController.id = '3175833810';  // 设定全局音乐播放ID
    APlayerController.volume = '0.7';
  </script>
  
    
<script src="https://cdn.jsdelivr.net/npm/aplayer@1.10/dist/APlayer.min.js"></script>

  
    
<script src="https://cdn.jsdelivr.net/npm/meting@2.0/dist/Meting.min.js"></script>

  




  
  
<script src="/lemonelf/js/valine.js"></script>


<script>
  var GUEST_INFO = ['nick', 'mail', 'link'];
  var meta = 'nick,mail,link'.split(',').filter(function (item) {
    return GUEST_INFO.indexOf(item) > -1
  });
  var REQUIRED_FIELDS = ['nick', 'mail', 'link'];
  var requiredFields = 'nick,mail'.split(',').filter(function (item) {
    return REQUIRED_FIELDS.indexOf(item) > -1
  });

  function emoji(path, idx, ext) {
    return path + "/" + path + "-" + idx + "." + ext;
  }

  var emojiMaps = {};
  for (var i = 1; i <= 54; i++) {
    emojiMaps['tieba-' + i] = emoji('tieba', i, 'png');
  }
  for (var i = 1; i <= 101; i++) {
    emojiMaps['qq-' + i] = emoji('qq', i, 'gif');
  }
  for (var i = 1; i <= 116; i++) {
    emojiMaps['aru-' + i] = emoji('aru', i, 'gif');
  }
  for (var i = 1; i <= 125; i++) {
    emojiMaps['twemoji-' + i] = emoji('twemoji', i, 'png');
  }
  for (var i = 1; i <= 4; i++) {
    emojiMaps['weibo-' + i] = emoji('weibo', i, 'png');
  }

  function pjax_valine() {
    var valinePath = $.trim($('#valine-path').text()) === "none" ?
            window.location.pathname : $.trim($('#valine-path').text());

    var valinePlaceholder = $.trim($('#valine-placeholder').text()) === "none" ?
            "快来评论吧~" : $.trim($('#valine-placeholder').text());

    var ALLPATH = '';
    if(ALLPATH != '') valinePath = ALLPATH;

    var valine = new Valine();
    valine.init({
      el: '#valine_container',
      meta: meta,
      placeholder: valinePlaceholder,
      path: valinePath,
      appId: "",
      appKey: "",
      pageSize: '10',
      avatar: 'robohash',
      lang: 'zh-cn',
      visitor: 'true',
      highlight: 'true',
      mathJax: 'false',
      enableQQ: 'true',
      requiredFields: requiredFields,
      emojiCDN: 'https://cdn.jsdelivr.net/gh/volantis-x/cdn-emoji/valine/',
      emojiMaps: emojiMaps
    })
  }

  $(function () {
    pjax_valine();
  });
</script>




<!-- darkmodejs -->


<!-- 复制 -->

  <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
    var clipboard = new ClipboardJS('.btn-copy', {
        target: function (trigger) {
            return trigger.nextElementSibling
        }
    });
    function wait(callback, seconds) {
        var timelag = null;
        timelag = window.setTimeout(callback, seconds)
    }
    function pjax_initCopyCode() {
        var copyHtml = '';
        copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
        copyHtml += '<i class="fas fa-copy"></i><span>COPY</span>';
        copyHtml += '</button>';
        $(".highlight .code pre").before(copyHtml);
        $(".article pre code").before(copyHtml);
        clipboard.off('success').on('success', function (e) {
            let $btn = $(e.trigger);
            $btn.addClass('copied');
            let $icon = $($btn.find('i'));
            $icon.removeClass('fa-copy');
            $icon.addClass('fa-check-circle');
            let $span = $($btn.find('span'));
            $span[0].innerText = 'COPIED';
            wait(function () {
                $icon.removeClass('fa-check-circle');
                $icon.addClass('fa-copy');
                $span[0].innerText = 'COPY'
            }, 2000)
        });
        clipboard.off('error').on('error', function (e) {
            e.clearSelection();
            let $btn = $(e.trigger);
            $btn.addClass('copy-failed');
            let $icon = $($btn.find('i'));
            $icon.removeClass('fa-copy');
            $icon.addClass('fa-times-circle');
            let $span = $($btn.find('span'));
            $span[0].innerText = 'COPY FAILED';
            wait(function () {
                $icon.removeClass('fa-times-circle');
                $icon.addClass('fa-copy');
                $span[0].innerText = 'COPY'
            }, 2000)
        })
    }
    $(function () {
        pjax_initCopyCode()
    });
</script>


<!-- scrollreveal -->

<!-- ******************************** -->

<!-- fancybox -->
<script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>
<script>
  function pjax_fancybox() {
    $(".article-entry").find("img").not('.inline').not('a img').each(function () { //渲染 fancybox
      var element = document.createElement("a"); // a 标签
      $(element).attr("class", "fancybox");
      $(element).attr("pjax-fancybox", "");  // 过滤 pjax
      $(element).attr("href", $(this).attr("src"));
      if ($(this).attr("data-original")) {
        $(element).attr("href", $(this).attr("data-original"));
      }
      $(element).attr("data-fancybox", "images");
      var caption = "";   // 描述信息
      if ($(this).attr('alt')) {  // 判断当前页面是否存在描述信息
        $(element).attr('data-caption', $(this).attr('alt'));
        caption = $(this).attr('alt');
      }
      var div = document.createElement("div");
      $(div).addClass("fancybox");
      $(this).wrap(div); // 最外层套 div ，其实主要作用还是 class 样式
      var span = document.createElement("span");
      $(span).addClass("image-caption");
      $(span).text(caption); // 加描述
      $(this).after(span);  // 再套一层描述
      $(this).wrap(element);  // 最后套 a 标签
    })
    $(".article-entry").find("img").fancybox({
      selector: '[data-fancybox="images"]',
      hash: false,
      loop: false,
      closeClick: true,
      helpers: {
        overlay: {closeClick: true}
      },
      buttons: [
        "zoom",
        "close"
      ]
    });
  };
  $(function () {
    pjax_fancybox();
  });
</script>

<!-- ******************************** -->


  
<script src="/lemonelf/js/app.js"></script>




  
    
<script src="/lemonelf/js/search.js"></script>

  



  <script defer src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-busuanzi@2.3/js/busuanzi.pure.mini.js" data-pjax></script>



  
<script src="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.js"></script>

  <script type="text/javascript">
    $(function () {
      Waves.attach('.flat-btn', ['waves-button']);
      Waves.attach('.float-btn', ['waves-button', 'waves-float']);
      Waves.attach('.float-btn-light', ['waves-button', 'waves-float', 'waves-light']);
      Waves.attach('.flat-box', ['waves-block']);
      Waves.attach('.float-box', ['waves-block', 'waves-float']);
      Waves.attach('.waves-image');
      Waves.init();
    });
  </script>



  
<script src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@2/js/comment_typing.js"></script>







    
      


<script src="https://cdn.jsdelivr.net/npm/pjax@0.2.8/pjax.min.js"></script>

<!-- 样式位于：source/css/_third-party/pjaxanimate.styl -->

<div class="pjax-animate">
  
    <script src="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js"></script>
    <div id="loading-bar-wrapper"><script>NProgress.configure({parent:"#loading-bar-wrapper",trickleSpeed: 100})</script></div>
    <script>
      window.ShowLoading = function() {
        NProgress.start();
      };
      window.HideLoading = function() {
        NProgress.done();
      }
    </script>
  
</div>

<script>
    var pjax;
    document.addEventListener('DOMContentLoaded', function () {
      pjax = new Pjax({
        elements: 'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([pjax-fancybox])',
        selectors: [
          "title",
          "#pjax-container",
          "#pjax-header-nav-list"
        ],
        cacheBust: false,   // url 地址追加时间戳，用以避免浏览器缓存
        timeout: 5000
      });
    });

    document.addEventListener('pjax:send', function (e) {
      window.stop(); // 相当于点击了浏览器的停止按钮

      try {
        var currentUrl = window.location.pathname;
        var targetUrl = e.triggerElement.href;
        var banUrl = [""];
        if (banUrl[0] != "") {
          banUrl.forEach(item => {
            if(currentUrl.indexOf(item) != -1 || targetUrl.indexOf(item) != -1) {
              window.location.href = targetUrl;
            }
          });
        }
      } catch (error) {}

      window.subData = null; // 移除标题（用于一二级导航栏切换处）
      $.fancybox.close();    // 关闭弹窗
      $('.nav-main').find('.list-v').not('.menu-phone').css("display","none"); // 移除小尾巴
      $('.menu-phone.list-v').css("display","none"); // 移除小尾巴
      $('.l_header .switcher .s-search').removeClass('active'); // 关闭移动端激活的搜索框
      $('.l_header').removeClass('z_search-open'); // 关闭移动端激活的搜索框
      $('.wrapper').removeClass('sub'); // 跳转页面时关闭二级导航

      $('.s-top').unbind('click');   // 解绑
      $('.menu a').unbind('click');  // 解绑
      $(window).unbind('resize');    // 解绑
      $(window).unbind('scroll');    // 解绑
      $(document).unbind('scroll');  // 解绑
      $(document).unbind('click');   // 解绑
      $('body').unbind('click');     // 解绑
      window.ShowLoading();
    });

    document.addEventListener('pjax:complete', function () {
      // 关于百度统计对 SPA 页面的处理：
      // 方案一：百度统计>管理>单页应用设置中，打开开启按钮即可对SPA进行统计。 https://tongji.baidu.com/web/help/article?id=324
      // 方案二：取消注释下列代码。 https://tongji.baidu.com/web/help/article?id=235
      // 

      // 关于谷歌统计对 SPA 页面的处理：
      // 当应用以动态方式加载内容并更新地址栏中的网址时，也应该更新通过 gtag.js 存储的网页网址。
      // https://developers.google.cn/analytics/devguides/collection/gtagjs/single-page-applications?hl=zh-cn
      

      $('.nav-main').find('.list-v').not('.menu-phone').removeAttr("style",""); // 移除小尾巴的移除
      $('.menu-phone.list-v').removeAttr("style",""); // 移除小尾巴的移除
      $('script[data-pjax], .pjax-reload script').each(function () {
        $(this).parent().append($(this).remove());
      });

      try{
        pjax_fancybox();
        
          
          if ('.cover') {
            $('.cover').backstretch("resize");
          } else {
            $.backstretch("resize");
          }
        
        
        
        
          pjax_initCopyCode();
        
        
          pjax_valine();
        
        
        
        
        
      } catch (e) {
        console.log(e);
      }
      window.HideLoading();
    });

    document.addEventListener('pjax:error', function (e) {
      window.HideLoading();
      window.location.href = e.triggerElement.href;
    });
</script>

    
  </div>
</body>
</html>
